package com.qjl.parser.logicflow;

import com.qjl.core.ELNode;
import com.qjl.core.ExpressLanguageParseException;
import com.qjl.parser.ELTransfer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LogicFLowTransfer implements ELTransfer {

	/**
	 * 待解析的LogicFlow数据
	 */
	private LogicFlowManager logicFlowManger;

	/**
	 * 上下文
	 */
	private LfParseContext context;

	private void initContext() {
		List<LfNode> nodes = logicFlowManger.getNodes();
		List<LfEdge> edges = logicFlowManger.getEdges();
		//sourceNodeId-> List<LfEdge>
		Map<String, List<LfEdge>> sourceNodeToEdgesMap = edges.stream().collect(Collectors.groupingBy(LfEdge::getSourceNodeId));
		//targetNodeId-> List<LfEdge>
		Map<String, List<LfEdge>> targetNodeToEdgesMap = edges.stream().collect(Collectors.groupingBy(LfEdge::getTargetNodeId));
		//nodeId -> LfNode
		Map<String, LfNode> nodeMap = nodes.stream().collect(Collectors.toMap(LfNode::getId, Function.identity(), (a, b) -> a));
		//获取所有when节点Map
		Map<String, LfNode> whenNodeMap = logicFlowManger.getAllWhenNodeMap();
		//获取所有外层when
		Set<LfNode> outermostLayerWhen = logicFlowManger.getOutermostLayerWhen();

		//根节点
		if (StringUtils.isEmpty(context.getRootId())) {
			String rootId = logicFlowManger.getRootId();
			context.setRootId(rootId);
		}

		context.setNodeMap(nodeMap);
		context.setWhenNodeMap(whenNodeMap);
		context.setSourceNodeToEdgesMap(sourceNodeToEdgesMap);
		context.setTargetNodeToEdgesMap(targetNodeToEdgesMap);
		context.setOutermostLayerWhen(outermostLayerWhen);
	}

	@Override
	public TransferNode transfer() {

		List<LfNode> nodes = logicFlowManger.getNodes();
		List<LfEdge> edges = logicFlowManger.getEdges();
		if (CollectionUtils.isEmpty(nodes) || CollectionUtils.isEmpty(edges)) {
			return null;
		}
		//初始化上下文
		this.initContext();
		TransferNode transferNode = initTsNode(context.getRootId());
		this.doTrans(transferNode);
		return transferNode;
	}


	public void doTrans(TransferNode transferNode) {
		String currentId = Optional.of(transferNode).map(TransferNode::getLfNode).map(LfNode::getId).orElse("");
		Map<String, List<LfEdge>> sourceNodeToEdgesMap = context.getSourceNodeToEdgesMap();
		Map<String, LfNode> nodeMap = context.getNodeMap();
		LfNode currentLfNode = nodeMap.get(currentId);
		String currentNodeType = currentLfNode.getLfNodeProperty(LfNodePropertyEnum.nodeType.name());

		if (ELNode.ELNameEnum.WHEN.name().equals(currentNodeType)) {
			this.processWhenInChildContext(currentId, transferNode, false);
			return;
		}

		//无子节点
		if (!logicFlowManger.hasNext(currentId, sourceNodeToEdgesMap)) {
			return;
		}

		if (logicFlowManger.hasOnlyOneNext(currentId, sourceNodeToEdgesMap)) {
			//寻找下一个子节点
			String nextId = logicFlowManger.getNextFirstOne(currentId, sourceNodeToEdgesMap);
			TransferNode transferNode1 = initTsNode(nextId);
			transferNode.addNext(transferNode1);
			this.doTrans(transferNode1);
		}

		if (logicFlowManger.hasManyNext(currentId, sourceNodeToEdgesMap)) {
			currentLfNode = nodeMap.get(currentId);
			currentNodeType = currentLfNode.getLfNodeProperty(LfNodePropertyEnum.nodeType.name());

			// 如果当前节点是common 而且是两个以上的子节点，考虑替换when来出来。
			if (ELNode.ELNameEnum.COMMON.name().equals(currentNodeType)) {
				//获取最外层的when
				Set<LfNode> outermostLayerWhen = context.getOutermostLayerWhen();
				Set<String> whenIds = outermostLayerWhen.stream().map(LfNode::getId).collect(Collectors.toSet());
				if (!CollectionUtils.isEmpty(whenIds)) {
					List<String> targetIds = logicFlowManger.getNextAll(currentId, sourceNodeToEdgesMap);
					String foundWhen = context.findWhenContainAnyOne(whenIds, targetIds);
					if (!StringUtils.isEmpty(foundWhen)) {
						this.processWhenInChildContext(foundWhen, transferNode, true);
					}
				}
				return;
			}

			List<String> nextAll = logicFlowManger.getNextAll(currentId, sourceNodeToEdgesMap);
			for (String nextId : nextAll) {
				TransferNode transferNode1 = initTsNode(nextId);
				transferNode.addNext(transferNode1);
				this.doTrans(transferNode1);
			}

		}


		return;

	}

	/**
	 * 在子上下文中处理when
	 *
	 * @param whenId
	 * @param transferNode
	 * @param addWhenNode
	 */
	private void processWhenInChildContext(String whenId, TransferNode transferNode, boolean addWhenNode) {
		Map<String, LfNode> nodeMap = context.getNodeMap();
		Map<String, List<LfEdge>> sourceNodeToEdgesMap = context.getSourceNodeToEdgesMap();
		LfNode whenLfNode = nodeMap.get(whenId);
		//递归析构出所有when内的所有节点
		List<String> allChildNodeInWhen = context.getAllChildNodeInWhen(whenLfNode);
		//继续执行when下一个节点
		String externalNodeIdOutSideWhen = logicFlowManger.doFindOutSideWhenNode(allChildNodeInWhen.get(0), sourceNodeToEdgesMap, new HashSet<>(allChildNodeInWhen));
		//递归获取下面所有子when
		Set<String> allChildWhenIds = context.recursiveWhenGetWhens(whenLfNode);
		List<String> childs = new ArrayList<>();
		childs.addAll(allChildWhenIds);
		childs.addAll(allChildNodeInWhen);


		TransferNode whenTsNode = initTsNode(whenId);
		//获取when内根节点
		Set<String> rootIdInWhen = context.getRootIdInWhen(whenLfNode);
		for (String r : rootIdInWhen) {
			//使用子容器进行处理
			LogicFlow childLogicFlow = new LogicFlow();
			childLogicFlow.setEdges(logicFlowManger.getEdges());
			childLogicFlow.setNodes(logicFlowManger.getNodesByIds(childs));
			LfParseContext lfParseContext = new LfParseContext();
			lfParseContext.setRootId(r);

			ELTransfer elTransfer = new LogicFLowTransfer(new LogicFlowManager(childLogicFlow), lfParseContext);
			TransferNode transferNodeFromChildTransfer = elTransfer.transfer();

			if (addWhenNode) {
				whenTsNode.addChild(transferNodeFromChildTransfer);
			} else {
				transferNode.addChild(transferNodeFromChildTransfer);
			}
		}

		if (addWhenNode) {
			transferNode.addNext(whenTsNode);
		}
		if (!StringUtils.isEmpty(externalNodeIdOutSideWhen)) {
			TransferNode transferNode1 = initTsNode(externalNodeIdOutSideWhen);
			if (addWhenNode) {
				whenTsNode.addNext(transferNode1);
			} else {
				transferNode.addNext(transferNode1);
			}
			this.doTrans(transferNode1);
		}
	}



	public TransferNode initTsNode(LfNode node) {
		TransferNode transferNode = new TransferNode();
		transferNode.setLfNode(node);
		return transferNode;
	}

	public TransferNode initTsNode(String id) {
		LfNode lfNode = context.getNodeMap().get(id);
		return initTsNode(lfNode);
	}

}
